package com.siteseat.crudeq.broker.netty;

import java.nio.channels.ClosedChannelException;
import java.util.Locale;

import org.apache.log4j.Logger;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelEvent;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.SimpleChannelHandler;

import com.siteseat.common.exception.ExternalException;
import com.siteseat.common.security.SecurityException;
import com.siteseat.crudeq.broker.transport.BrokerSession;
import com.siteseat.crudeq.broker.transport.BrokerSessionManager;
import com.siteseat.crudeq.common.raw.protocol.v1.ErrorCode;

/**
 * 
 */
public class BrokerSessionHandler extends SimpleChannelHandler {

  private Logger               log = Logger.getLogger( BrokerSessionHandler.class );

  private BrokerSessionManager sessionManager;

  /**
   * @param sessionManager
   */
  public BrokerSessionHandler( BrokerSessionManager sessionManager ) {
    this.sessionManager = sessionManager;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void channelOpen( ChannelHandlerContext ctx, ChannelStateEvent e ) throws Exception {
    Channel c = e.getChannel();
    this.sessionManager.create( c );
    ctx.sendUpstream( e );
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void handleDownstream( ChannelHandlerContext ctx, ChannelEvent e ) throws Exception {
    updateSessionAccessTime( e );
    super.handleDownstream( ctx, e );
  }

  /**
   * @param e
   */
  protected void updateSessionAccessTime( ChannelEvent e ) {
    Channel c = e.getChannel();
    if( c.isConnected() ) {
      BrokerSession s = this.sessionManager.get( c.getId() );
      if( s != null ) {
        s.updateAccessTime();
      }
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void handleUpstream( ChannelHandlerContext ctx, ChannelEvent e ) throws Exception {
    updateSessionAccessTime( e );
    super.handleUpstream( ctx, e );
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void channelDisconnected( ChannelHandlerContext ctx, ChannelStateEvent e ) throws Exception {
    this.sessionManager.remove( e.getChannel().getId() );
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void channelClosed( ChannelHandlerContext ctx, ChannelStateEvent e ) throws Exception {
    this.sessionManager.remove( e.getChannel().getId() );
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void exceptionCaught( ChannelHandlerContext ctx, ExceptionEvent e ) throws Exception {
    Channel c = e.getChannel();

    Throwable z = e.getCause();
    if( z instanceof ClosedChannelException ) {
      log.info( "Channel " + c + " is closed by client" );
      return;
    }
    log.error( z.getMessage(), z );
    
    if( c.isConnected() && c.isWritable() ) {
      if( z instanceof ExternalException ) {
        BrokerSession s = this.sessionManager.get( e.getChannel().getId() );
        Locale loc = ( s != null ) ? s.getLocale() : null;
        CodecHelper.writeError( c, ErrorCode.INVALID_REQUEST, ( (ExternalException)z ).getMessage( loc ) );
      } else if( z instanceof SecurityException ) {
        CodecHelper.writeError( c, ErrorCode.SECURITY_ERROR, z.getMessage() );
      } else {
        CodecHelper.writeError( c, ErrorCode.INTERNAL_ERROR );
      }
    } else {
      //c.close();
    }
  }

}
